#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include "sockets.h"
	

	
	
/* ************************** REVISAR CONEXIONES **************************************** */
/*seg=-1--> Blok, seg=0--> Instantanea, seg>0-->timeVal*/
/*-1--> vectorConexiones Vacia, 1--> OK, 0 timeValExpirado, -2 Algo Raro*/

int revisarConexiones(int vectorConexiones[],int tamanio,int segDeEsperaMaxima,int *socketAct){

int valorRetornoSelect;
int i,k;
int socketMayor=-1;
struct timeval espera;
fd_set conjuntoConexiones;
static int turno=0;

/*Planificamos el orden De Comprovacion para evitar inanicion*/
if (turno==tamanio-1) turno=0;
else turno++;


/* Borramos cualquier basura De conjuntoConexiones (fd_set)*/
FD_ZERO(&conjuntoConexiones);

/*sacamos el vector mayor*/
for(i=0;i<=tamanio-1;i++) if(vectorConexiones[i]>socketMayor) socketMayor=vectorConexiones[i];

/* SI estaba vacia retornamos -1*/
if (socketMayor==-1) return -1;

/*agregamos los sockets al conjunto requerido por select*/
for(i=0;i<=tamanio-1;i++) 
	if(vectorConexiones[i]!=-1) 
		FD_SET(vectorConexiones[i],&conjuntoConexiones);

/* cargarmos el tiempo*/
espera.tv_sec=segDeEsperaMaxima;
espera.tv_usec=0;

/* Todo lo demas */

while(1){

if(segDeEsperaMaxima>=0){
valorRetornoSelect=select(socketMayor+1,&conjuntoConexiones,NULL,NULL,&espera);
}
if(segDeEsperaMaxima<0){
valorRetornoSelect=select(socketMayor+1,&conjuntoConexiones,NULL,NULL,NULL);
}
if(valorRetornoSelect==-1){
	if(errno==EINTR){
		/*Loguear("mshell","[EINTR]","Reestableciendo...");*/
		continue;
	}
	else{	/*Loguear("mshell","[ERROR]","Revisar Funcion revisarConexiones()");*/
		printf("[ERROR]""Revisar Funcion revisarConexiones()");
		return -3;}
}
else if(valorRetornoSelect==0){
	return 0;
	/*Sale Por Timeval*/
}	

else break;
}


for(i=turno,k=0;i<=tamanio-1;i++,k++){
	
	if(vectorConexiones[i]!=-1){
		if(FD_ISSET(vectorConexiones[i],&conjuntoConexiones)){
			*socketAct=vectorConexiones[i];
			return 1;
			}
		
	}
	else{
			if((i+1)>(tamanio-1)) i=-1;
			if(k>tamanio) break;
	}
	
}
/* No deberia pasar Esto*/
/*printf("no deberia pasar esto= Turno=%d,vectorConexiones[0]=%d,vectorConexiones[1]=%d,i=%d, k=%d\n",
turno,vectorConexiones[0],vectorConexiones[1],i,k);
if(FD_ISSET(0,&conjuntoConexiones)) printf("deberia haber actividad en el socket 0");
*/
return -2;
}






/**************FUNCION PARA INICIALIZAR EL CONJUNTO DE CONEXIONES*********************/
int inicializarConjuntoDeConexiones(int vectorConexiones[],int tamanio){
int i;
for(i=0;i<=tamanio-1;i++){
	vectorConexiones[i]=-1;
}
return 0;
}





/* ***************FUNCION PARA AGREGAR CONEXIONES A UN CONJUNTO******************** */
/* , 0--> vector LLENO, 1--> OK*/
int agregarConexion(int vectorConexiones[],int tamanio, int socket){
int i;
i=0;
while (vectorConexiones[i]!=-1 && i<=(tamanio-1)) i++;
if (i<=(tamanio-1)){
	vectorConexiones[i]=socket;
	return 1;}
else return 0;
}









/* ********* FUNCION PARA VERIFICAR EXISTENCIA DE UN SOCKET EN EL CONJUNTO ****************** */
/* 1--> Encuentra, 0 No Encuentra*/
int existeConexion(int vectorConexiones[],int tamanio,int socket){
int i;

for(i=0;i<=tamanio-1;i++) if(vectorConexiones[i]==socket) return 1;

return 0;
}







/* ************FUNCION PARA ELIMAR UN SOCKET DEL CONJUNTO************************ */
/* 1--> OK, 0 No lo encontro*/
int eliminarConexion(int vectorConexiones[],int tamanio,int socket){
int i;
int ubicacion;
int socketAMover;

/* busca el socket a eliminar*/
for(i=0;(i<=(tamanio-1));i++) if(vectorConexiones[i]==socket) break;

/* si no lo encuenta retorna 0, si lo encuentra guarda la "ubicacion"*/
if(i<=(tamanio-1)) ubicacion=i;
else return 0;

/* busca el ultimo socket para moverlo a la posicion del eliminado*/
i=0;
while (vectorConexiones[i]!=-1 && i<=(tamanio-1)) i++;
/* guarda la ubicacion en socketAmover*/
socketAMover=vectorConexiones[i-1];

close(vectorConexiones[ubicacion]);
vectorConexiones[ubicacion]=socketAMover;
vectorConexiones[i-1]=-1;
return 1;
}


/* ************************* CONECTARSE **********************
 Es NO blokeante
 Hara 1 intento por conectarse a Partir de Un puerto y un IP, 
 Escribe en socketaAsociar
 socketaasociar es una variable limpia sin la llamada a socket
 
 TOMA puertoDestino IpDestino
 ESCRIBE en SocketAasociar con el Socket conectado, listo para ser usado
 DEVUELVE
 OK			---> Devuelve  0
 Error en Socket()	---> Devuelve -1
 Error en Connect()	---> Devuelve -2
*/

int conectarse(int* puertodestino, char* ipdestino,int* socketaasociar){

struct sockaddr_in dirdestino;

/* Creacion Del Socket */
if((*socketaasociar=socket(AF_INET,SOCK_STREAM,0))==-1) return -1;

/*Armado De La Estructura Destino */	
dirdestino.sin_addr.s_addr=inet_addr(ipdestino);
dirdestino.sin_port=htons(*puertodestino);
dirdestino.sin_family=AF_INET;
memset(&(dirdestino.sin_zero),'\0',8);

/* Se Intentara Conectarse al IP=%s\n",inet_ntoa(dirdestino.sin_addr.s_addr)); */
/* En El Puerto=%d\n",ntohs(dirdestino.sin_port) */
if(connect(*socketaasociar,(struct sockaddr*)&dirdestino,sizeof(struct sockaddr))==-1){
	return -2;}

return 0;
}

 
/* ********************** ESCUCHARYACEPTARVARIAS ***********************
 
   Escucha y acepta una cantidad (tomada como argumento) de conexiones entrantes.
   
   TOMA  mi puerto, mi ip , cantidad de conecciones a aceptar y tiempoMaximoDeEspera
   ESCRIBE los Sockets Entrantes en socketConectados, y sus respectivos datos en entrantesDir.
   	   por ORDEN DE LLEGADA DE LAS CONEXIONES

	NOTA: Es importante haber reservado memoria para los vectores
	      Recomendado inicio de procesos con X cantidad de conexiones con otros procesos
 	      Si tiempo<0 La funcion Se blokeara y esperara hasta aceptar toda la cantidad
	      Si tiempo==0 La funcion Comprobara y aceptara las que halla
	      
 * Devuelve >0 : Conexiones conectadas
 * Devuelve  0 : Salio Por Timeval y No se conecto nadie
 * Devuelve -1 : Error en socket()
 * Devuelve -2 : Error en Bind()
 * Devuelve -3 : Error en listen()
 * Devuelve -4 : Error en Accept()
 * Devuelve -5 : Error No evaluado En Select()
 * Devuelve -6 : Error No Evaluado
 */


int escucharyaceptarvarias(int* mipuerto,char* miip,int cantidad,int tiempo,
		int* socketconectados, struct sockaddr_in* entrantesdir){

int sockescucha;
struct sockaddr_in midir;
int tamaniodelsockaddr_in;
int i,valorSelect;
char modo;
struct timeval espera;

fd_set conjuntoescucha;

/* Creacion Del Socket Escucha */
if ((sockescucha=socket(AF_INET,SOCK_STREAM,0))==-1) return -1;


/* Llenado De la Estructura Escucha Sockaddr*/ 
midir.sin_family= AF_INET;
midir.sin_port= htons( *mipuerto);
memset(&midir.sin_zero,'\0',8);
midir.sin_addr.s_addr=inet_addr(miip);


/*Asociacion De la Estructura Escucha Con El Socket [bind()]*/
if (bind(sockescucha, (struct sockaddr *)&midir, sizeof (midir)) == -1){
		close(sockescucha);
		return -7;}


		
/* "Asignacion del Socket como un socket para Escuchar escuchar [Listen()] */
tamaniodelsockaddr_in=sizeof(struct sockaddr_in);
if (listen(sockescucha,cantidad)==-1){
	close(sockescucha);
	return -3;
	}
/* Elecccion Del Modo Blokeante o no Blokeante */ 
if(tiempo<0) modo='B';
else{
	modo='N';
	espera.tv_sec=tiempo;
	espera.tv_usec=0;
}



/* Aceptacion De las Conexiones Por Orden De llegada y EN FORMA BLOKEANTE*/
if(modo=='B'){
	for(i=1;i<=cantidad;i++){
	 if(((socketconectados[i-1])=accept(sockescucha,(struct sockaddr *)&entrantesdir[i-1],
					    (unsigned int*)&tamaniodelsockaddr_in))==-1)
	 	{
		close(sockescucha);
		return -4;
		}
	}
	close(sockescucha);
	return cantidad;
	}

/* Aceptacion De las Conexiones Por Orden De llegada y EN FORMA NO BLOKEANTE con timeval*/
if(modo=='N'){

	for(i=1;i<=cantidad;i++){
	  FD_ZERO(&conjuntoescucha);
	  FD_SET(sockescucha,&conjuntoescucha);
	
	  valorSelect=select((sockescucha+1),&conjuntoescucha,NULL,NULL,&espera);
	
	  if( (valorSelect==-1) && ((errno==EINTR) || (errno==EAGAIN)) ) { usleep(100); i--;}
	  else if(valorSelect==0){ 
		  		close(sockescucha);
		  		return (i-1);
	  			}
	  else if(valorSelect!=-1){
	     	if(((socketconectados[i-1])=accept(sockescucha,(struct sockaddr *)&entrantesdir[i-1], (unsigned int*)&tamaniodelsockaddr_in))==-1)
		{
		close(sockescucha);
		return -4;
		}
	  }
	else {close(sockescucha); return -5; /* Error no contemplado en select*/}
	}
	close(sockescucha);
	return (i-1);
}
close(sockescucha);
return -6;
}



/********************************* MANDARMENSAJECHAR ************************************/

/*  	TOMA una cadena de maximo 254 Caracteres LA EMPAQUETA (anteponiendo un char "tamanio")
 *  	y la envia a SOCKETDESTINO
 *  	Devuelve -1: Error De memoria
	Devuelve -2: Error en el envio del mensaje
	Devuelve  0: OK
 */

int mandarMensajeChar(char* mensaje,int* socketdestino){

int tamaniomensajeempaquetado;
char* mensajeempaquetado;

tamaniomensajeempaquetado=strlen(mensaje)+2;

if((mensajeempaquetado=(char*)calloc(tamaniomensajeempaquetado,sizeof(char)))==NULL) return -1;

mensajeempaquetado[0]=(char)tamaniomensajeempaquetado;
mensajeempaquetado[1]='\0';
strcat(mensajeempaquetado,mensaje);

if(!(send(*socketdestino,mensajeempaquetado,tamaniomensajeempaquetado,0)==tamaniomensajeempaquetado)) return -2;

free(mensajeempaquetado);
return 0;
}




/*---------------------------- LEER MENSAJE EMPAQUETADO ------------------------*/

int leerMensajeEmpaquetado(char*msj,int Sockbus){
	
int i;

char tamaniototal;

if((i=recv(Sockbus,&tamaniototal,1,0))==-1){
	/*Error en funcion Recv();*/
	perror("Error");
	printf("leerMensajeEmpaquetado()-ErrorEnRecv");
	
	return -1;}

	
else if(i==1){ 
	char aux[10];
	sprintf(aux,"%d",(int)tamaniototal);
	}

else if(i==0) return -2; /* Cerraron la conexion */

else{
  printf("leerMensajeEmpaquetado()-ERROR3");
  return -3;}

/*mensaje Thola
 * T=5
 * cadena leida hasta msj[3] 
 * aplicamos en \0 en msj[4] --> tamanio-1
 * */

if((i=recv(Sockbus,msj,(int)tamaniototal-1,0))==((int)tamaniototal-1)){
	msj[tamaniototal-1]='\0';
	return 0; /*OK!!*/
	  }
    	  
 else if(i<((int)tamaniototal-1)){
    printf("[LeerMensajeBuscador-ERROR]-Recepcion Incompleta Del Mensaje");
    return -1;}

printf("leerMensajeEmpaquetado()-ERROR4");

return -4;
}



int descubrirConexion(int socket,char * host,char * ip,char * pto){

struct sockaddr_in Datos1ConexionRemota;

struct hostent *Datos2ConexionRemota;	

int TamanioSockaddr=sizeof(struct sockaddr);

if((Datos2ConexionRemota=(struct hostent*)malloc(sizeof(struct hostent)))==NULL){
	fprintf(stderr,"Errror De Malloc() en Descubrir Conexion\n");
	return -2;
	}

if(getpeername(socket,(struct sockaddr*)&Datos1ConexionRemota,(socklen_t*)&TamanioSockaddr)==-1){
	fprintf(stderr,"Error en getpeername() en Descubrir Conexion\n");
	return -2;
}

strcpy(ip,(char*)inet_ntoa(Datos1ConexionRemota.sin_addr));

sprintf(pto,"%d",ntohs(Datos1ConexionRemota.sin_port));

if((Datos2ConexionRemota=gethostbyaddr(ip,strlen(ip),AF_INET))==NULL){
int  k=0;
  while(1){
  if(h_errno==HOST_NOT_FOUND){
	/*fprintf(stderr,"Error en gethostbyaddr() Host No Encontrado\n");*/
 	break;
  	}
  else if((h_errno==NO_ADDRESS)||(h_errno==NO_DATA)){ 
	/*fprintf(stderr,"Error en gethostbyaddr() NO_ADDRESS or NO_DETA\n");*/
  	break;  	
  	}
  else if(h_errno==NO_RECOVERY){
	break;
	/*fprintf(stderr,"Error en gethostbyaddr() NO_RECOVERY\n");*/
  	}
  else if(h_errno==TRY_AGAIN){
	k++;
	/*fprintf(stderr,"Error en gethostbyaddr() TRY_AGAIN\n");*/
  	usleep(1000);
  	if(k==2) break;
  	}
  }
 return -1;}

strcpy(host,(*Datos2ConexionRemota).h_name);
return 0;
}


/* ********* CREAR SOCKET ESCUCHA ********* */
int crearSocketEscucha(int* mipuerto,char* miip,int *socketEscucha){
int sockescucha;
struct sockaddr_in midir;



/* Creacion Del Socket Escucha */
if ((sockescucha=socket(AF_INET,SOCK_STREAM,0))==-1) return 0;

/* Llenado De la Estructura Escucha Sockaddr*/ 
midir.sin_family= AF_INET;
midir.sin_port= htons( *mipuerto);
memset(&midir.sin_zero,'\0',8);
midir.sin_addr.s_addr=inet_addr(miip);


/*Asociacion De la Estructura Escucha Con El Socket [bind()]*/
if (bind(sockescucha, (struct sockaddr *)&midir, sizeof (midir)) == -1){
		close(sockescucha);
		return 0;}
		
/* "Asignacion del Socket como un socket para Escuchar escuchar [Listen()] */

if (listen(sockescucha,10)==-1){
	close(sockescucha);
	return 0;
	}
*socketEscucha=sockescucha;

return 1;
}


int escucharyaceptarvarias2(int sockescucha,int cantidad,int tiempo,
		int* socketconectados, struct sockaddr_in* entrantesdir){
	
int tamaniodelsockaddr_in;
int i,valorSelect;
char modo;
struct timeval espera;

fd_set conjuntoescucha;


tamaniodelsockaddr_in=sizeof(struct sockaddr_in);


/* Elecccion Del Modo Blokeante o no Blokeante */ 
if(tiempo<0) modo='B';
else{
	modo='N';
	espera.tv_sec=tiempo;
	espera.tv_usec=0;
}

/* Aceptacion De las Conexiones Por Orden De llegada y EN FORMA BLOKEANTE*/
if(modo=='B'){
	for(i=1;i<=cantidad;i++){
	 if(((socketconectados[i-1])=accept(sockescucha,(struct sockaddr *)&entrantesdir[i-1],
					     (unsigned int*)&tamaniodelsockaddr_in))==-1)
	 	{
		return -4;
		}
	}
	return cantidad;
	}

/* Aceptacion De las Conexiones Por Orden De llegada y EN FORMA NO BLOKEANTE con timeval*/
if(modo=='N'){

	for(i=1;i<=cantidad;i++){
	  FD_ZERO(&conjuntoescucha);
	  FD_SET(sockescucha,&conjuntoescucha);
	
	  valorSelect=select((sockescucha+1),&conjuntoescucha,NULL,NULL,&espera);
	
	  if( (valorSelect==-1) && ((errno==EINTR) || (errno==EAGAIN)) ) { usleep(100); i--;}
	  else if(valorSelect==0){ 
		  		return (i-1);
	  			}
	  else if(valorSelect!=-1){
	     	if(((socketconectados[i-1])=accept(sockescucha,(struct sockaddr *)&entrantesdir[i-1],  (unsigned int*)&tamaniodelsockaddr_in))==-1)
		{
		return -4;
		}
	  }
	else {close(sockescucha); return -5; /* Error no contemplado en select*/}
	}

	return (i-1);
}
return -6;
}

